import graphene
from django.utils import timezone
from graphene import relay
from graphql.error import GraphQLError
from graphql_jwt.decorators import login_required
from graphql_relay import from_global_id
from graphql_relay.node.node import to_global_id

from home.push.tasks import get_enable_reg_ids_except_user, push_to_users

from .models import Comment, Topic
from .types import CommentType, TopicType
from .utils import unmark


class AddTopicMutation(relay.ClientIDMutation):
    class Input:
        title = graphene.String(required=True, description="话题的标题")
        description = graphene.String(required=True, description="话题的说明")

    topic = graphene.Field(TopicType)

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, **input):
        title = input.get("title")
        description = input.get("description")

        topic = Topic(
            title=title,
            description=description,
            user=info.context.user,
            is_open=True,
        )

        topic.edited_at = timezone.now()
        topic.save()

        reg_ids = get_enable_reg_ids_except_user(topic.user)
        if reg_ids:
            push_to_users.delay(
                reg_ids,
                f"{topic.user.username} 发布新话题",
                f"{topic.title}\n{unmark(topic.description)}",
                f'/topic/{to_global_id("TopicType", topic.id)}',
                True,
            )

        return AddTopicMutation(topic=topic)


class DeleteTopicMutation(relay.ClientIDMutation):
    class Input:
        topic_id = graphene.ID(required=True, description="话题的 ID")

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, **input):
        _, topic_id = from_global_id(input.get("topic_id"))

        try:
            topic = Topic.objects.get(pk=topic_id)
        except Topic.DoesNotExist:
            raise GraphQLError("话题不存在")

        if topic.user != info.context.user:
            raise GraphQLError("只能删除自己创建的话题")
        topic.delete()

        return DeleteTopicMutation()


class UpdateTopicMutation(relay.ClientIDMutation):
    class Input:
        id = graphene.ID(required=True, description="话题的 ID")
        title = graphene.String(description="话题的标题")
        description = graphene.String(description="话题的说明")

    topic = graphene.Field(TopicType)

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, **input):
        _, topic_id = from_global_id(input.get("id"))
        title = input.get("title")
        description = input.get("description")

        try:
            topic = Topic.objects.get(pk=topic_id)
        except Topic.DoesNotExist:
            raise GraphQLError("话题不存在")

        if topic.user != info.context.user:
            raise GraphQLError("只能修改自己创建的话题")

        # 仅在传入数据时修改
        if title is not None:
            topic.title = title
        if description is not None:
            topic.description = description

        topic.edited_at = timezone.now()
        topic.save()
        return UpdateTopicMutation(topic=topic)


class CloseTopicMutation(relay.ClientIDMutation):
    class Input:
        topic_id = graphene.ID(required=True, description="话题的 ID")

    topic = graphene.Field(TopicType)

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, **input):
        _, topic_id = from_global_id(input.get("topic_id"))

        try:
            topic = Topic.objects.get(pk=topic_id)
        except Topic.DoesNotExist:
            raise GraphQLError("话题不存在")

        topic.is_open = False
        topic.closed_at = timezone.now()
        topic.save()
        return CloseTopicMutation(topic=topic)


class ReopenTopicMutation(relay.ClientIDMutation):
    class Input:
        topic_id = graphene.ID(required=True, description="话题的 ID")

    topic = graphene.Field(TopicType)

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, **input):
        _, topic_id = from_global_id(input.get("topic_id"))

        try:
            topic = Topic.objects.get(pk=topic_id)
        except Topic.DoesNotExist:
            raise GraphQLError("话题不存在")

        topic.is_open = True
        topic.save()
        return ReopenTopicMutation(topic=topic)


class PinTopicMutation(relay.ClientIDMutation):
    class Input:
        topic_id = graphene.ID(required=True, description="话题的 ID")

    topic = graphene.Field(TopicType)

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, **input):
        _, topic_id = from_global_id(input.get("topic_id"))

        try:
            topic = Topic.objects.get(pk=topic_id)
        except Topic.DoesNotExist:
            raise GraphQLError("话题不存在")

        topic.is_pin = True
        topic.save()
        return PinTopicMutation(topic=topic)


class UnpinTopicMutation(relay.ClientIDMutation):
    class Input:
        topic_id = graphene.ID(required=True, description="话题的 ID")

    topic = graphene.Field(TopicType)

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, **input):
        _, topic_id = from_global_id(input.get("topic_id"))

        try:
            topic = Topic.objects.get(pk=topic_id)
        except Topic.DoesNotExist:
            raise GraphQLError("话题不存在")

        topic.is_pin = False
        topic.save()
        return UnpinTopicMutation(topic=topic)


class AddCommentMutation(relay.ClientIDMutation):
    class Input:
        topic_id = graphene.ID(required=True, description="属于的话题 ID")
        body = graphene.String(required=True, description="评论的内容")
        parent_id = graphene.ID(description="上一级评论的 ID")

    comment = graphene.Field(CommentType)

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, **input):
        _, topic_id = from_global_id(input.get("topic_id"))
        body = input.get("body")
        parent_id = input.get("parent_id")

        try:
            topic = Topic.objects.get(pk=topic_id)
        except Topic.DoesNotExist:
            raise GraphQLError("话题不存在")

        if not topic.is_open:
            raise GraphQLError("无法向关闭的话题添加评论")

        comment = Comment(
            topic=topic,
            user=info.context.user,
            body=body,
        )
        if parent_id:
            _, parent_id = from_global_id(parent_id)
            parent_comment = Comment.objects.get(pk=parent_id)
            # 若回复层级超过二级，则转换为二级
            comment.parent_id = parent_comment.get_root().id
            # 被回复人
            comment.reply_to = parent_comment.user
        comment.save()

        reg_ids = get_enable_reg_ids_except_user(comment.user)
        if reg_ids:
            push_to_users.delay(
                reg_ids,
                f"{comment.topic.title} 下有新回复",
                f"{comment.user.username}：{unmark(comment.body)}",
                f'/topic/{to_global_id("TopicType", topic.id)}',
                True,
            )

        return AddCommentMutation(comment=comment)


class DeleteCommentMutation(relay.ClientIDMutation):
    class Input:
        comment_id = graphene.ID(required=True, description="评论的 ID")

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, **input):
        _, comment_id = from_global_id(input.get("comment_id"))

        try:
            comment = Comment.objects.get(pk=comment_id)
            if comment.user != info.context.user:
                raise GraphQLError("只能删除自己创建的评论")
            comment.delete()
            return DeleteCommentMutation()
        except Comment.DoesNotExist:
            raise GraphQLError("评论不存在")


class UpdateCommentMutation(relay.ClientIDMutation):
    class Input:
        id = graphene.ID(required=True, description="评论的 ID")
        body = graphene.String(description="评论的内容")

    comment = graphene.Field(CommentType)

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, **input):
        _, comment_id = from_global_id(input.get("id"))
        body = input.get("body")

        try:
            comment = Comment.objects.get(pk=comment_id)
        except Comment.DoesNotExist:
            raise GraphQLError("评论不存在")

        if comment.user != info.context.user:
            raise GraphQLError("只能修改自己创建的评论")

        comment.body = body
        comment.save()
        return UpdateCommentMutation(comment=comment)
